
package com.neusoft.neuiotms.dcom.service.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.Transactional;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.neusoft.bizcore.auth.common.bean.UserBean;
import com.neusoft.bizcore.notice.constant.NoticeConsts;
import com.neusoft.bizcore.notice.notification.dto.NoticeDingDTO;
import com.neusoft.bizcore.notice.notification.dto.NoticeRecordDTO;
import com.neusoft.bizcore.notice.notification.dto.NoticeRecordSystemDTO;
import com.neusoft.bizcore.web.constant.ExceptionConstants;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.bizcore.web.support.Searchable;
import com.neusoft.bizcore.web.utils.EasyPoiStylerColorImpl;
import com.neusoft.bizcore.web.utils.JsonUtils;
import com.neusoft.neuiotms.dcom.converter.MonthWeeklyConverter;
import com.neusoft.neuiotms.dcom.dto.InstitutionsDTO;
import com.neusoft.neuiotms.dcom.dto.MonthWeeklyDTO;
import com.neusoft.neuiotms.dcom.model.MonthServiceSummary;
import com.neusoft.neuiotms.dcom.model.MonthWeekly;
import com.neusoft.neuiotms.dcom.repository.MonthServiceSummaryRepository;
import com.neusoft.neuiotms.dcom.repository.MonthUnfinishedItemDescRepository;
import com.neusoft.neuiotms.dcom.repository.MonthValueAddedRepository;
import com.neusoft.neuiotms.dcom.repository.MonthWeeklyRepository;
import com.neusoft.neuiotms.dcom.service.InstitutionsService;
import com.neusoft.neuiotms.dcom.service.MonthUnfinishedItemDescService;
import com.neusoft.neuiotms.dcom.service.MonthValueAddedService;
import com.neusoft.neuiotms.dcom.service.MonthWeeklyService;
import com.neusoft.neuiotms.dcom.util.PermUtils;
import com.neusoft.neuiotms.micro.notice.NoticeMicroService;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.word.WordExportUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class MonthWeeklyServiceImpl implements MonthWeeklyService {

    @Autowired
    private MonthWeeklyRepository monthWeeklyRepository;
    @Autowired
    private MonthWeeklyConverter monthWeeklyConverter;
    @Autowired
    private MonthValueAddedService monthValueAddedService;
    @Autowired
    private MonthUnfinishedItemDescService monthUnfinishedItemDescService;
    @Autowired
    private MonthServiceSummaryRepository monthServiceSummaryRepository;
    @Autowired
    private MonthUnfinishedItemDescRepository monthUnfinishedItemDescRepository;
    @Autowired
    private MonthValueAddedRepository monthValueAddedRepository;
    @Autowired
    private InstitutionsService institutionsService;
    @PersistenceContext
    private EntityManager entityManager;
    @Autowired
    private NoticeMicroService noticeMicroService;

    @Override
    public List<MonthWeeklyDTO> index(final Searchable searchable, final UserBean userBean) {
        final List<InstitutionsDTO> institutions = this.institutionsService.getPermInstitutions(userBean);
        final List<String> institutionCodeArray =
                institutions.stream().map(it -> it.getCode()).collect(Collectors.toList());
        searchable.put("institutionCodeArray", institutionCodeArray);
        if (PermUtils.isWorker(userBean)) {
            searchable.put("residentEngineerUserCode", userBean.getUsername());
        }
        final List<MonthWeekly> models = this.monthWeeklyRepository.index(searchable);
        return this.monthWeeklyConverter.toListDTO(models);
    }

    @Override
    public Page<MonthWeeklyDTO> search(final Pageable pageable, final Searchable searchable, final UserBean userBean) {
        final List<InstitutionsDTO> institutions = this.institutionsService.getPermInstitutions(userBean);
        final List<String> institutionCodeArray =
                institutions.stream().map(it -> it.getCode()).collect(Collectors.toList());
        searchable.put("institutionCodeArray", institutionCodeArray);
        if (PermUtils.isWorker(userBean)) {
            searchable.put("residentEngineerUserCode", userBean.getUsername());
        }
        final Page<MonthWeekly> result = this.monthWeeklyRepository.search(pageable, searchable);
        return this.monthWeeklyConverter.toPageDTO(result);
    }

    @Override
    public MonthWeeklyDTO create(final MonthWeeklyDTO dto) {

        final MonthWeekly model = this.monthWeeklyConverter.toModel(dto);
        this.monthWeeklyRepository.save(model);

        return this.monthWeeklyConverter.toDTO(model);
    }

    @Override
    public MonthWeeklyDTO update(final Long id, final MonthWeeklyDTO dto) {

        MonthWeekly model;

        try {
            model = this.monthWeeklyRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        dto.setId(model.getId());
        model = this.monthWeeklyConverter.toModel(dto);
        this.monthWeeklyRepository.save(model);

        return this.monthWeeklyConverter.toDTO(model);
    }

    @Override
    public MonthWeeklyDTO details(final Long id) {

        MonthWeekly model;

        try {
            model = this.monthWeeklyRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        final MonthWeeklyDTO dto = this.monthWeeklyConverter.toDTO(model);
        dto.setAddedValueList(this.monthValueAddedService.getInfoByMonthId(id));
        dto.setUnFinishedList(this.monthUnfinishedItemDescService.getInfoByMonthWeeklyId(id));
        return dto;
    }

    @Transactional
    @Override
    public void delete(final Long id) {

        this.monthServiceSummaryRepository.deleteByMonthId(id);
        this.monthUnfinishedItemDescRepository.deleteByMonthId(id);
        this.monthValueAddedRepository.deleteByMonthId(id);
        this.monthWeeklyRepository.deleteById(id);

    }

    @Override
    public XWPFDocument export(final Long id) {
        final Map<String, Object> map = new HashMap<>();
        if (id == null) {
            throw new UnitedException("导出月报出错，必须选择一条数据");
        }
        final MonthWeeklyDTO dto = this.details(id);
        map.put("project", dto.getProjectName());
        map.put("client", dto.getClientName());
        map.put("operation", dto.getOperationUserName());
        map.put("engineer", dto.getResidentEngineerUserName());
        map.put("mobile", dto.getMobileEngineerUserName());
        map.put("serviceMonth", dto.getServiceMonth());

        if (dto.getOperationTime() != null) {
            final Calendar calendar = Calendar.getInstance();
            calendar.setTime(dto.getOperationTime());
            map.put("y", calendar.get(Calendar.YEAR));
            map.put("m", calendar.get(Calendar.MONTH) + 1);
        }
        map.put("month", dto.getServiceMonth());
        map.put("wanDisconnect", dto.getUnplannedWanOutageDuration());
        map.put("wanAvailable", dto.getWanMonthlyAvailability());
        map.put("lanDisconnect", dto.getUnplannedLanOutageDuration());
        map.put("lanAvailable", dto.getLanMonthlyAvailability());
        map.put("extraDisconnet", dto.getUnplannedWanOutageDuration());
        map.put("extraAvailable", dto.getExtranetMonthlyAvailability());
        map.put("resTotalTime", dto.getResidentServiceTotalTime());
        map.put("allTotalNumber", dto.getServiceTotalNumber());
        map.put("allTotalTime", dto.getServiceTotalTime());
        map.put("customerSatisfaction", dto.getCustomerSatisfaction());
        map.put("weeklySummary", dto.getWeeklySummary());
        map.put("major", dto.getMajorIncidentReport());
        map.put("nextMonthWorkPlan", dto.getNextMonthWorkPlan());
        map.put("suggestion", dto.getReasonableSuggestion());
        map.put("remark", dto.getRemark());
        map.put("list", dto.getUnFinishedList());
        //拼接各个服务分项
        final List<MonthServiceSummary> list = this.monthServiceSummaryRepository.findByMonthId(id);
        //        final Map<String, List<MonthServiceSummary>> sListMap =
        //                list.stream().collect(Collectors.toMap(MonthServiceSummary::getServiceTypeName,
        //                        p -> {
        //                            final List<MonthServiceSummary> summaryList = new ArrayList<>();
        //                            summaryList.add(p);
        //                            return summaryList;
        //                        },
        //                        (final List<MonthServiceSummary> value1, final List<MonthServiceSummary> value2) -> {
        //                            value1.addAll(value2);
        //                            return value1;
        //                        }));
        //
        //        final List<ExportSummaryList> sList = new ArrayList<>();
        //        for (final Map.Entry<String, List<MonthServiceSummary>> entry : sListMap.entrySet()) {
        //            final ExportSummaryList temp = new ExportSummaryList();
        //            temp.setServiceTypeName(entry.getKey());
        //            entry.getValue().forEach(t -> {
        //                temp.getSub()
        //                        .add(new ExportSummray(t.getServiceAttributesName(), t.getTotalNumber(), t.getTotalTime()));
        //            });
        //            sList.add(temp);
        //        }
        map.put("sList", list);
        map.put("vList", dto.getAddedValueList());
        try {
            final XWPFDocument doc = WordExportUtil.exportWord07(
                    "word/HN.docx", map);
            return doc;
        } catch (final Exception e) {
            throw new UnitedException("导出月报出错");
        }

    }

    @Getter
    @Setter
    class ExportSummaryList {
        private String serviceTypeName;
        List<ExportSummray> sub = new ArrayList<>();
    }

    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    class ExportSummray {
        private String serviceAttributesName;
        private Integer totalNumber;
        private Integer totalTime;
    }

    @Override
    public List<InstitutionsDTO> unstartdInstitutions() {
        final List<InstitutionsDTO> institutions = this.institutionsService.index(new Searchable());
        final Date start = DateUtils.truncate(Calendar.getInstance().getTime(), Calendar.MONTH);
        final List<MonthWeekly> list = this.monthWeeklyRepository.findAllByOperationTime(start);
        return institutions.stream()
                .filter(it -> list.stream().filter(w -> it.getCode().equals(w.getClientCode())).count() == 0)
                .collect(Collectors.toList());
    }

    @Override
    public Map<String, Long> reportStats() {
        final List<InstitutionsDTO> institutions = this.institutionsService.index(new Searchable());

        final Calendar c = Calendar.getInstance();
        c.set(Calendar.DAY_OF_MONTH, 1);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        final Date start = c.getTime();

        final List<MonthWeekly> list = this.monthWeeklyRepository.findAllByOperationTime(start);

        final Map<String, Long> result = Maps.newHashMap();
        final long reportedCount = list.stream().map(it -> it.getClientCode()).distinct().count();
        result.put("已提交", reportedCount);
        result.put("未提交", institutions.size() - reportedCount);

        return result;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Page<Map<String, Object>> report(Pageable pageable, final String username, final Date start,
            final Date end) {

        final String countSql = this.buildTotalSql(username, start, end);
        MonthWeeklyServiceImpl.log.info(countSql);
        final Query query1 = this.entityManager.createNativeQuery(countSql);
        query1.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        final List<Map<String, Object>> result1 = query1.getResultList();
        int total = 0;
        if (result1.size() > 0) {
            total = Integer.parseInt(String.valueOf(result1.get(0).get("count")));
        }

        final String sql = this.buildQuerySql(pageable, username, start, end);
        MonthWeeklyServiceImpl.log.info(sql);
        final Query query = this.entityManager.createNativeQuery(sql);
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        final List<Map<String, Object>> result = query.getResultList();
        return new PageImpl<>(result, pageable, total);
    }

    @SuppressWarnings("unchecked")
    @Override
    public Workbook exportReport(final String username, final Date start, final Date end) {
        final String sql = this.buildExportSql(username, start, end);
        MonthWeeklyServiceImpl.log.info(sql);
        final Query query = this.entityManager.createNativeQuery(sql);
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        final List<Map<String, Object>> result = query.getResultList();

        final List<WeekReportVO> list = new ArrayList<>(result.size() == 0 ? 1 : result.size());
        result.stream().map(t -> {
            final WeekReportVO vo = new WeekReportVO();
            vo.setUser_name(t.get("operation_user_name"));
            vo.setTotal_times(t.get("total_times"));
            return vo;
        }).forEach(list::add);

        final ExportParams param =
                new ExportParams("周报统计", "周报统计", ExcelType.XSSF);
        param.setStyle(EasyPoiStylerColorImpl.class);
        return ExcelExportUtil.exportExcel(param, WeekReportVO.class, list);
    }

    private String buildTotalSql(final String username, final Date start, final Date end) {
        final String statement = " count(*) as count ";
        return this.buildSql(username, start, end, statement);
    }

    private String buildQuerySql(Pageable pageable, final String username, final Date start, final Date end) {
        final String statement = " * ";
        String noPageSql = this.buildSql(username, start, end, statement);
        noPageSql += " limit " + pageable.getPageSize() + " offset " + pageable.getOffset();

        return noPageSql;
    }

    private String buildExportSql(final String username, final Date start, final Date end) {
        final String statement = " * ";
        return this.buildSql(username, start, end, statement);
    }

    private String buildSql(final String username, final Date start, final Date end, String statement) {
        String where = "";
        if (StringUtils.isNotBlank(username)) {
            where += "and operation_user_code='" + username + "' ";
        }
        if (null != start) {
            where += "and operation_time>='" + DateFormatUtils.format(start, "yyyy-MM-dd") + "' ";
        }
        if (null != end) {
            final Calendar c = Calendar.getInstance();
            c.setTime(end);
            c.add(Calendar.DAY_OF_MONTH, 1);
            where += "and operation_time<'" + DateFormatUtils.format(c.getTime(), "yyyy-MM-dd") + "' ";
        }

        String sql = "SELECT "
                + statement
                + "From ( "
                + "SELECT "
                + "  operation_user_name, "
                + "  count(*) AS total_times "
                + "FROM "
                + "  neuiot_month_weekly "
                + "WHERE "
                + "  operation_user_name IS NOT NULL "
                + "  AND operation_user_name <> '' "
                + "  %s "
                + "GROUP BY "
                + "  operation_user_name "
                + ") t ";

        sql = String.format(sql, where, where, where, where);

        return sql;
    }

    @Setter
    @Getter
    static class WeekReportVO {

        @Excel(name = "人员", orderNum = "0")
        private Object user_name;

        @Excel(name = "填报次数", orderNum = "1")
        private Object total_times;

    }

    @Override
    public void checkReportNotice() {
        final List<InstitutionsDTO> unstartdInstitutions = this.unstartdInstitutions();
        final List<String> users = Lists.newArrayList();
        unstartdInstitutions.forEach(it -> {
            final List<String> usernameArray =
                    it.getResidents().stream().map(r -> r.getUsername()).collect(Collectors.toList());
            users.addAll(usernameArray);
        });
        if (users.size() == 0) {
            return;
        }
        final NoticeRecordDTO notice = new NoticeRecordDTO();
        notice.setCreateTime(new Date());
        notice.setAppCode("dcom");
        notice.setType(Lists.newArrayList(NoticeConsts.INFORM_TYPE_NOTICE, NoticeConsts.INFORM_TYPE_DINGDING));
        notice.setTitle("月报提醒");
        final NoticeRecordSystemDTO toSystem = new NoticeRecordSystemDTO();
        toSystem.setNoticeType("月报提醒");
        toSystem.setNoticeRoute("hn_monthWeekly");
        toSystem.setUsers(users);
        toSystem.setContent("请填写工作月报");
        notice.setSystem(toSystem);

        final NoticeDingDTO toDing = new NoticeDingDTO();
        toDing.setMsgtype("text");
        toDing.setUsers(users);
        final Map<String, String> linkParams = Maps.newHashMap();
        linkParams.put("content", "请填写工作月报");
        toDing.setJsonBody(JsonUtils.pojoToJson(linkParams));
        notice.setDing(toDing);

        this.noticeMicroService.createNotification(JsonUtils.pojoToJson(notice));

    }
}
